from PyQt5.QtGui import QPainter, QPaintEvent, QColor, QPen, QMouseEvent
from PyQt5.QtWidgets import QWidget, QMenu, QAction
from PyQt5.QtCore import Qt, QRectF, QPointF, QPoint
import time
import math
import numpy as np
import Utility.CubicSpline as CubicSpline


class HistogramCanvasWidget(QWidget):
    histogramData: np.ndarray = None  # 灰度直方图数据,除以最大值归一化后，1xn 的数组
    color = QColor(255, 255, 255, 255)  # 画笔颜色

    def __init__(self, parent):
        super().__init__(parent)
        self.initUI()

    def initUI(self):
        # self.setStyleSheet("background-color: #0000FF;")
        # self.setAttribute(Qt.WA_StyledBackground, True)
        self.setWindowFlags(Qt.FramelessWindowHint)

    def paintEvent(self, event: QPaintEvent):
        if self.histogramData is None:
            return
        data = self.histogramData
        color = self.color
        w, h = self.width(), self.height()
        padding = (0, 0, 0, 0)  # 上下左右
        w2 = w - padding[2] - padding[3]
        h2 = h - padding[0] - padding[1]
        # 绘制 直方图
        col = self.histogramData.shape[0]  # 数据列数
        spin = w2 / 256 * math.ceil(256 / col)  # 界面步进
        height = h2  # 直方图 最大高度

        qp = QPainter()
        qp.begin(self)
        for i in range(0, col - 1):
            rect = QRectF(padding[2] + i * spin, padding[0] + (1 - data[i]) * height, spin,
                          data[i] * height)  # 左上角x,yRate w,h
            qp.fillRect(rect, color)
        # pen = QPen(Qt.blue, 5, Qt.SolidLine)
        # qp.setPen(pen)
        # 绘制最后一个数据项
        i = col - 1
        spin = w / 256 * (256 % col)  # 步进+
        rect = QRectF(w2 + padding[2] - spin, padding[0] + (1 - data[i]) * height, spin, data[i] * height)  # 左上角x,yRate w,h
        qp.fillRect(rect, self.color)
        qp.end()


class ControllerTag(QWidget):
    # 父对象
    Parent: QWidget = None
    # 索引
    Index = 0
    xRate = 0   # x百分比位置
    yRate = 0   # y百分比位置

    def __init__(self, parent):
        super().__init__(parent)
        self.Parent = parent
        self.initUI()

    def initUI(self):
        # self.setStyleSheet("background-color: #0000FF;")
        # self.setAttribute(Qt.WA_StyledBackground, True)
        self.setWindowFlags(Qt.FramelessWindowHint)
        self.resize(8, 8)
        self.show()

    def paintEvent(self, event: QPaintEvent):
        # 绘制圆形
        qp = QPainter()
        pen = QPen(Qt.blue, 5, Qt.SolidLine)
        qp.begin(self)
        # qp.setPen(pen)
        qp.fillRect(self.rect(), Qt.blue)
        # qp.drawRect(self.rect())
        # qp.drawEllipse(self.width() / 2, self.height() / 2, 5, 5)
        # qp.drawPoint(self.width()/2, self.height()/2)
        # qp.drawPixmap(self.rect(), QPixmap(':/tool/tool-pen.png'))
        qp.end()

    def mousePressEvent(self, event: QMouseEvent):
        if event.button() == Qt.LeftButton:
            print('tag: 按下鼠标左键, index:', self.Index)
            self.Parent.Active = True
            self.Parent.editingTag = self
            event.accept()

    def mouseReleaseEvent(self, event: QMouseEvent):
        if event.button() == Qt.LeftButton:
            print('tag: 释放鼠标左键')
            self.Parent.Active = False
            self.Parent.editingTag = None
            self.Parent.updateSlot()
            event.accept()

    def move2(self, x, y):
        """
        移动部件中心到（x,y)
        :param x: int
        :param y: int
        :return:
        """
        self.xRate = x / self.parent().width()
        self.yRate = y / self.parent().height()
        x = x - self.width() / 2
        y = y - self.height() / 2
        self.move(x, y)

    def updatePos(self):
        x = self.parent().width() * self.xRate
        y = self.parent().height() * self.yRate
        x = x - self.width() / 2
        y = y - self.height() / 2
        self.move(x, y)

    @property
    def pos2(self):
        """
        中心坐标
        :return:
        """
        return QPoint(self.x2() + self.width() / 2, self.y2() + self.height() / 2)

    @property
    def x2(self):
        """
        自然坐标系(向右为x正轴，向上为y正轴)，部件中心点坐标
        :return:
        """
        return self.x() + self.width() / 2

    @property
    def y2(self):
        """
        部件中心点坐标
        :return:
        """
        return self.y() + self.height() / 2


class CurveCanvasWidget(QWidget):
    histogramData = None  # 灰度直方图数据,除以最大值归一化后，1xn 的数组
    color = (QColor(200, 200, 200), QColor(220, 20, 60), QColor(60, 179, 113), QColor(0, 191, 255))  # 画笔颜色
    channel = 0  # 当前通道
    tagList = None  # tag列表 (全部RGB) 四个方法
    curveDataList = [None, None ,None ,None]  # 灰度级映射数据 曲线(全,R,G,B)
    Active = False  # 为true时更新曲线数据
    editingTag = None  # 正在操作的tag
    moveCnt = 0  # 移动计数,每移动三次计算一次数据

    # 部件
    context = None
    dialog = None  # 主对话框
    contextTag = None   # 当前context目标tag
    contextPos = None # 当前context点

    def __init__(self, parent):
        super().__init__(parent)
        self.tagList = [[], [], [], []]  # tag列表 (全部RGB) 四个方法
        self.curveDataList = [None, None, None, None]  # 灰度级映射数据 曲线(全,R,G,B)
        self.moveCnt = 0  # 移动计数,每移动三次计算一次数据
        # 部件
        self.context = None
        self.dialog = None  # 主对话框
        self.contextTag = None  # 当前context目标tag
        self.contextPos = None  # 当前context点
        self.initUI()

    def initUI(self):
        self.setAttribute(Qt.WA_DeleteOnClose, True)
        self.setWindowFlags(Qt.FramelessWindowHint)
        self.initTag()
        self.initContext()

    def initContext(self):
        # Content
        menu = QMenu(self)
        self.context = menu

        Act = QAction("新建", menu)
        menu.addAction(Act)
        Act.triggered.connect(self.__newTagAction)
        menu.newTagAct = Act

        Act = QAction("删除", menu)
        menu.addAction(Act)
        Act.triggered.connect(self.__delTagAction)
        menu.delTagAct = Act

    def contextMenuEvent(self, event):
        self.context.popup(self.mapToGlobal(event.pos()))
        self.contextPos = event.pos()
        tag = self.childAt(event.x(), event.y())
        self.contextTag = tag
        if tag:
            self.context.newTagAct.setVisible(False)
            self.context.delTagAct.setVisible(True)
        else:
            self.context.delTagAct.setVisible(False)
            self.context.newTagAct.setVisible(True)

    def __newTagAction(self):
        if self.contextPos:
            x = self.contextPos.x()
            # 寻找合适的列表位置
            tags = self.tagList[self.channel]
            length = len(tags)
            index = -1
            for i in range(0, length-1):
                if tags[i].x() < x < tags[i + 1].x():  # 找到合适位置
                    index = i
                    break
            if index > -1:
                # 插入
                tag = self.newTag(x, self.contextPos.y())
                tags.insert(index + 1, tag)
                self.calCurveData()
                self.update()
            else:
                print('插入失败')

    def __delTagAction(self):
        if self.contextTag:
            self.tagList[self.channel].remove(self.contextTag)
            self.contextTag.deleteLater()
            self.calCurveData()
            self.update()

    def initTag(self):
        w = self.width()
        h = self.height()
        self.tagList[self.channel].append(self.newTag(0., h))
        self.tagList[self.channel].append(self.newTag(w/2, h/2))
        self.tagList[self.channel].append(self.newTag(w, 0.))
        self.calCurveData()

    def updateSlot(self):
        self.dialog.updateChannelData()

    def newTag(self, x, y):
        tag = ControllerTag(self)
        tag.move2(x, y)
        return tag

    def changeChanel(self, channel):
        # 隐藏当前显示的tag
        for tag in self.tagList[self.channel]:
            tag.hide()
        # 显示更新后的当前tag
        self.channel = channel
        if len(self.tagList[channel]) == 0:
            self.initTag()
        else:
            for tag in self.tagList[self.channel]:
                tag.show()

    def mouseMoveEvent(self, event: QMouseEvent):
        if self.Active:
            if self.editingTag:
                x = event.pos().x()
                y = event.pos().y()
                # 边界检查
                if x > self.width():
                    x = self.width()
                if y > self.height():
                    y = self.height()
                # 相邻锚点检查
                tags = self.tagList[self.channel]
                index = tags.index(self.editingTag)
                if 0 < index < len(tags) - 1:
                    if x > tags[index + 1].x2:
                        x = tags[index + 1].x2 - 1
                    elif x < tags[index - 1].x2:
                        x = tags[index - 1].x2 + 1
                elif index == 0:    # 左端点限制
                    x = 0
                elif index == len(tags) - 1:    # 又端点限制
                    x = self.width()

                self.editingTag.move2(x, y)
                if self.moveCnt > 6:
                    self.moveCnt = 0
                    self.calCurveData()
                    self.update()
                else:
                    self.moveCnt = self.moveCnt + 1

    def calCurveData(self):
        tags = self.tagList[self.channel]
        num = len(tags)  # tag数量
        if num <= 1:
            return
        w, h = self.width(), self.height()
        # 绘制 曲线
        spinW = w / 255  # 界面W步进
        spinH = h / 255  # 界面W步进

        tagX = np.zeros(num)  # tag x坐标
        tagY = np.zeros(num)  # tag y坐标
        for i in range(0, num):
            x = tags[i].x2
            y = h - tags[i].y2  # 自然坐标系(向右为x正轴，向上为y正轴)
            # 转化为灰度级整数坐标
            x = int(x / spinW)
            y = int(y / spinH)
            tagX[i] = x
            tagY[i] = y

        # 生成 样条曲线
        anchor = np.zeros((2, num))
        anchor[0, :] = tagX
        anchor[1, :] = tagY
        curveX, curveY = CubicSpline.generatorCubicSpline(anchor, 1)
        curveY = np.uint8(np.clip(curveY, 0, 255))
        num = curveX.shape[0]  # 点数量
        data = np.zeros((2, num))
        data[0, :] = curveX
        data[1, :] = curveY
        self.curveDataList[self.channel] = data

    def paintEvent(self, event: QPaintEvent):
        tags = self.tagList[self.channel]
        num = len(tags)  # tag数量
        if num <= 1:
            return
        # 绘图
        curveX = self.curveDataList[self.channel][0, :] * self.width() / 256
        curveY = self.height() - self.curveDataList[self.channel][1, :] * self.height() / 256
        num = curveX.shape[0]  # 点数量
        pen = QPen(self.color[self.channel], 2, Qt.SolidLine)
        qp = QPainter()
        qp.begin(self)
        qp.setPen(pen)
        for i in range(0, num - 1):
            # 画线
            qp.drawLines(QPointF(curveX[i], curveY[i]), QPointF(curveX[i + 1], curveY[i + 1]))
        qp.end()

    def resizeEvent(self, event):
        for tag in self.tagList[self.channel]:
            tag.updatePos()
        self.calCurveData()
        # self.update()
